<template>
  <q-form ref="QxForm" :class="className" @reset="onReset" @submit="onSubmit">
    <template v-if="formWidth > 0 || isSearch">
      <template v-for="(column, index) in columns(option.column)" :key="getKey(column)">
        <q-expansion-item v-if="column.children" :class="expansionCls(column)" :disable="loading"
                          :group="`group-${index}`" :label="column.label" class="qx-form-children" default-opened
                          switch-toggle-side>
          <q-card class="row q-col-gutter-sm">
            <template v-for="cColumn in columns(column.children)" :key="getKey(cColumn)">
              <form-item v-model="formInfo" :column="formatColumn(cColumn, isSearch)" :dense="isDense"
                         :formWidth="formWidth" :isAuto="option?.isAuto" :isSearch="isSearch" :nonTop="nonTop"
                         v-bind="columnOption(cColumn)">
                <template v-for="item in formatSlots(cColumn.field)" v-slot:[item]="{ attrs, column: col }">
                  <slot :attrs="attrs" :column="col" :name="item"></slot>
                </template>
              </form-item>
            </template>
          </q-card>
        </q-expansion-item>
        <form-item v-else v-model="formInfo" :column="formatColumn(column, isSearch)" :dense="isDense"
                   :formWidth="formWidth" :isAuto="option?.isAuto" :isSearch="isSearch" :nonTop="nonTop"
                   v-bind="columnOption(column)">
          <template v-for="item in formatSlots(column.field)" v-slot:[item]="{ attrs, column: col }">
            <slot :attrs="attrs" :column="col" :name="item"></slot>
          </template>
        </form-item>
      </template>
      <div v-if="showBtn" :class="menuCls">
        <q-btn color="primary" type="submit" unelevated v-bind="menuBind" v-bind:outline="false"
               v-bind:text-color="isSearch ? BgToColor('primary') : 'white'">
          <template v-if="isSearch">
            <q-icon name="search"></q-icon>
            {{ option.searchText || $t('Search') }}
          </template>
          <template v-else>
            <q-icon name="published_with_changes"></q-icon>
            {{ option.submitText || $t('Submit') }}
          </template>
        </q-btn>
        <q-btn v-if="option.resetBtn !== false" type="reset" v-bind="menuBind">
          <q-icon name="restore"></q-icon>
          {{ option.resetText || $t('Reset') }}
        </q-btn>
        <slot :scope="menuBind" name="menuRight"></slot>
      </div>
    </template>
  </q-form>
  <q-resize-observer debounce="100" @resize="onResize" />
</template>

<script setup>
import { formatColumn } from 'components/formUtils.js'
import FormItem from 'components/Non-Global/FormItem.vue'
import { isEmpty } from 'src/utils/utils'
import { computed, inject, ref, useSlots, watchPostEffect } from 'vue'

const props = defineProps({
  class: String, nonTop: Boolean, isSearch: Boolean,
  dense: { type: Boolean, default: true },
  option: { type: Object, default: () => ({}) }
})
const SLOTS = Object.keys(useSlots())
const BgToColor = inject('BgToColor')
const emits = defineEmits(['reset', 'submit'])
const isDense = computed(() => props.dense ?? inject('isDense', true))

const formWidth = ref(0)
const QxForm = ref(null)
const loading = ref(false)
const getKey = computed(() => column => column?.label + column?.field)
const formInfo = defineModel('modelValue', { type: Object, default: () => ({}) })
const menuBind = computed(
  () => ({ 'no-wrap': true, padding: '.4rem 1rem', loading: loading.value, outline: true, 'text-color': 'primary' }))
const className = computed(() => {
  let cls = 'qx-form row items-start rounded-borders'
  const { class: classes, isSearch } = props
  if (classes) cls += ` ${classes}`
  if (isSearch) return `${cls} qx-form-search`
  return `${cls} qx-form-normal`
})
const columns = computed(
  () => (columns = []) => columns.filter(el => props.isSearch ? el.search !== false : el.display !== false))
const columnOption = computed(() => {
  return (column = {}) => {
    const { labelPosition, labelWidth, searchLabelWidth, span, searchSpan, readonly } = props.option || {}
    if (props.isSearch) {
      const searchLabelW = isEmpty(column.searchLabelWidth) ? searchLabelWidth : column.searchLabelWidth
      return {
        loading: loading.value, labelPosition: 'right', readonly: column.searchReadonly,
        span: column.searchSpan || searchSpan,
        labelWidth: isEmpty(searchLabelW) ? (column.labelWidth || labelWidth) : searchLabelW
      }
    }
    return {
      loading: loading.value, span: column.span || span, labelPosition: column.labelPosition || labelPosition,
      readonly: isEmpty(column.readonly) ? readonly : column.readonly,
      labelWidth: isEmpty(column.labelWidth) ? labelWidth : column.labelWidth
    }
  }
})
const formatSlots = computed(() => {
  return field => SLOTS.filter(
    el => [field, `${field}A`, `${field}B`, `${field}H`, `${field}P`, `${field}AP`].includes(el))
})
const showBtn = computed(() => {
  const { menuBtn, searchMenuBtn = true } = props.option || {}
  return props.isSearch ? searchMenuBtn : menuBtn
})
const expansionCls = computed(() => {
  return column => {
    const cls = column.expandCls || props.option.expandCls || ''
    if (column.span) return `q-col-${column.span} ${cls}`
    if (props.option?.span) return `q-col-${props.option.span} ${cls}`
    return `q-col-24 ${cls}`
  }
})
const formatPosition = position => {
  if (position === 'left') return 'justify-start'
  if (position === 'right') return 'justify-end'
  return 'justify-center'
}
const menuCls = computed(() => {
  const { searchMenuSpan, searchMenuPosition, menuSpan, menuPosition } = props.option || {}
  let cls = 'row q-gutter-md no-wrap items-center'
  if (isDense.value) cls += ' qx-form-menu-dense'
  else cls += ' qx-form-menu'
  if (props.isSearch) {
    if (searchMenuSpan) cls += ` q-col-${searchMenuSpan}`
    else cls += ' col'
    if (searchMenuPosition) cls += ` ${formatPosition(searchMenuPosition)}`
    return cls
  }
  if (menuSpan) cls += ` q-col-${menuSpan} q-pl-md`
  else cls += ' q-col-24'
  cls += ` ${formatPosition(menuPosition)}`
  return cls
})

const onResize = size => size.width && (formWidth.value = size.width)
const initForm = () => QxForm.value && QxForm.value.resetValidation()
watchPostEffect(() => initForm())
const onReset = () => {
  if (QxForm.value) QxForm.value.resetValidation()
  formInfo.value = {}
  emits('reset')
}
const done = () => loading.value = false
const validate = (callback, error) => {
  QxForm.value.validate().then(success => {
    if (success) {
      // 是的，模型是正确的
      if (callback) {
        loading.value = true
        callback(formInfo.value, done)
      }
    } else error && error()
  })
}
const onSubmit = () => {
  loading.value = true
  emits('submit', formInfo.value, done)
}
defineExpose({ onReset, validate })
</script>